#include <GA\Task\UniTranspTask.hpp>
#include <algorithm>

unitraf::UniTranspTask::UniTranspTask( size_t routeNBits, size_t routeSizeBits, size_t routesSize, size_t routeMaxN , size_t busesMax)
  : _routeNBits( routeNBits ), _routeSizeBits( routeSizeBits ), _routesSize( routesSize ), _routeMaxN( routeMaxN ), _busesMax( busesMax ) {

    typedef graph_type::edge_type Edge;
    typedef graph_type::vertex_type Vertex;
  
    enum vertices { A = 0x0, B, C, D, E, F, G, H, J, I, K, L, M, N, O, P };

    Vertex vertex_array[] = {
      Vertex(A, 10.0), Vertex(B, 10.0), Vertex(C, 10.0), Vertex(D, 10.0), 
      Vertex(E, 10.0), Vertex(F, 10.0), Vertex(G, 10.0), Vertex(H, 10.0), 
      Vertex(J, 10.0), Vertex(I, 10.0), Vertex(K, 10.0), Vertex(L, 10.0), 
      Vertex(M, 10.0), Vertex(N, 10.0), Vertex(O, 10.0), Vertex(P, 10.0)
    };

    Edge edge_array[] = {
      Edge(A, B, 5.21), Edge(A, E, 4.41),
      Edge(B, F, 23.17), Edge(B, C, 100.18),
      Edge(C, D, 4.11), Edge(C, G, 8.30),
      Edge(D, H, 13.75),
      Edge(E, F, 59.91), Edge(E, J, 6.11),
      Edge(F, G, 7.01), Edge(F, I, 21.57),
      Edge(G, K, 25.68), Edge(G, H, 7.16),
      Edge(H, L, 7.90),
      Edge(J, I, 6.74), Edge(J, M, 6.89),
      Edge(I, N, 1.84), Edge(I, K, 32.24),
      Edge(K, O, 7.83), Edge(K, L, 11.32),
      Edge(L, P, 0.7),
      Edge(M, N, 9.32),
      Edge(N, O, 45.94),
      Edge(O, P, 7.47)
    };

    // Graph initializing

    _graph.InsertVertices(vertex_array, vertex_array + sizeof(vertex_array) / sizeof(Vertex));
      
    _graph.InsertEdges(edge_array, edge_array + sizeof(edge_array) / sizeof(Edge));

    // Scheduler initializing

    _scheduler._init(_graph);

    // Generate all applicable routes
    
    graph_type::model_type::out_vertex_pairii vertices = _graph.GetOutVertices();
    
    _scheduler._find_routes(vertices.first, vertices.last, P, 7);

};

//routes[i] = size( how many buses have this route ), + n ( number of the route )
void unitraf::UniTranspTask::decode( BinaryString *BS, vector<RouteDesc> &routes) const
{
	if ( routes.size( ) != routesSize( ) )
		routes.resize( routesSize( ) );

	for ( size_t i = 0; i < routes.size( ); ++i )
	{
		size_t begin = routeBits( ) * i;
		routes[i]._size = BS->toUlong( begin , begin + routeSizeBits( ) );
		routes[i]._n = BS->toUlong( begin + routeSizeBits( ), begin + routeBits( ));
	}
}


void unitraf::UniTranspTask::encode( BinaryString *BS, vector<RouteDesc> &routes) const
{
	if ( routes.size( ) != routesSize( ) )
		routes.resize( routesSize( ) );

	for ( size_t i = 0; i < routes.size( ); ++i )
	{
		size_t begin = i * routeBits( );
		RouteDesc route( routes[i] );
		
		for( size_t j = 0; j < routeSizeBits( ); ++j )
		{
			BS->setBit( begin + routeSizeBits( ) - 1 - j, route._size & 1 );
			route._size = route._size >> 1;
		}

		for ( size_t j = 0; j < routeNBits( ); ++j )
		{
			BS->setBit( begin + routeBits( ) - 1 - j, route._n & 1 );
			route._n = route._n >> 1;
		}
	}
}

void unitraf::UniTranspTask::makeValid( BinaryString *BS ) const
{
	vector<RouteDesc> routes( routesSize( ) );
	decode( BS, routes );

	size_t buses = 0;

	for ( size_t i = 0; i < routesSize( ); ++i )
	{
		routes[i]._n = routes[i]._n % routeMaxN( );
		buses += routes[i]._size;
	}

  encode(BS, routes);

	vector<Fit> fit;
	
    fitness(BS, &fit);

		std::sort( fit.begin( ), fit.end( ) );

	if ( buses < busesMax( ) )
	{
		size_t n = fit[0].n( ); //best route number;
		routes[n]._size += busesMax( ) - buses; //increase best route number
	}

	if ( buses > busesMax( ) )
	{
		size_t extraBuses = buses - busesMax( );

		//route network extra buses decrease algorithm
		for ( size_t i = 0; i < fit.size( ); ++i )
		{
			size_t n = fit[fit.size( ) - i - 1].n( ); // current worst route number
      //if current worst route contain more busses than extraBuses
			if ( routes[n]._size > extraBuses )
			{
				//decrease current worst route size  
				routes[n]._size -= extraBuses;
				extraBuses = 0;
				break;//stop the algorithm
			}
			else //if current worst route contain less busses than extraBuses
			{
				extraBuses -= routes[n]._size - 1;//decrease extraBuses
				routes[n]._size = 1; //decrease current worst route size to 1
				//continue the algorithm
			}
		}
	}
	

	encode( BS, routes );
}

double unitraf::UniTranspTask::fitness( BinaryString *BS, vector<Fit> * routes_values) const
{
	
  vector<RouteDesc> routes( routesSize( ) );
	decode( BS, routes );
  
  double total_value;

  if (routes_values) {
    total_value = _scheduler._assess(routes, *routes_values);
  } else {
    vector<Fit> _stub;
    total_value = _scheduler._assess(routes, _stub);
  };

  return total_value;
}



